﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using TrainingReminderSystem.Model;
using Fleck;
using Extentions;
using System.Web;
using System.Threading;
using Microsoft.EntityFrameworkCore;

namespace TrainingReminderSystem.Bll
{
    /// <summary>
    /// WebSocket消息管理类 必须先调用Start方法
    /// </summary>
    public class WebSocketBll
    {
        private static WebSocketHelper _webSocketHelper = null;

        /// <summary>
        /// WebSocket是否启动成功
        /// </summary>
        public static bool WebSocketSuccess { get; set; } = false;

        /// <summary>
        /// 启动 失败一次后,10秒后再次调用
        /// </summary>
        public static void Start(int count = 0)
        {
            const int maxCount = 2;
            if (count > maxCount) return;
            int port = SysConfig.WebSocketPortChat;
            if (port <= 0) return;
            string fileName = !string.IsNullOrEmpty(SysConfig.Pfx) ? (Base64Helper.CurrentDirectory + SysConfig.Pfx) : string.Empty;
            Task.Run(() =>
            {
                if (_webSocketHelper != null)
                {
                    Close();
                }
                bool useWss = SysConfig.Wss && !string.IsNullOrEmpty(fileName) && !string.IsNullOrEmpty(SysConfig.PfxPwd);
                string url = $"{(useWss ? "wss" : "ws")}://0.0.0.0:{port}/Chat";
                try
                {
                    X509Certificate2 obj = null;
                    if (useWss)
                    {
                        //X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
                        obj = new X509Certificate2(fileName, SysConfig.PfxPwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
                    }
                    _webSocketHelper = new Extentions.WebSocketHelper();
                    _webSocketHelper.ConnectingEvent += _webSocketHelper_ConnectingEvent;
                    _webSocketHelper.ClosingEvent += _webSocketHelper_ClosingEvent;
                    _webSocketHelper.ReceiveMessageEvent += _webSocketHelper_ReceiveMessageEvent;

                    var socket = _webSocketHelper.Start(url, obj);
                    $"{DateTime.Now.ExToLong()}websocket开启成功:{url}".ExLog();
                    WebSocketSuccess = true;
                }
                catch (Exception e)
                {
                    Task.Run(() =>
                    {
                        Thread.Sleep(10000);
                        //启动失败,再次启动
                        Close();
                        Start(count + 1);
                    });
                    string msg = $"{DateTime.Now.ExToLong()}咨询订单的websocket开启失败:{url}\r\n{e.Message}\r\n异常:{e.ExFullString()}";
                    msg.ExLog();
                    e.ExLogError($"{DateTime.Now.ExToLong()}咨询订单的websocket开启失败:{url}");
                }
            });
        }

        #region 私有方法

        private static void _webSocketHelper_ReceiveMessageEvent(IWebSocketConnection socket, string message)
        {
            Task.Run(() =>
            {
                string logMsg = $"websocket接收信息出错:{DateTime.Now.ExToLong()};对方信息:{socket.ConnectionInfo.ClientIpAddress};\r\n{socket.ConnectionInfo.ExToJson()};\r\n消息内容:{message};";
                //得到用户信息 或 员工信息
                WebSocketInfo info = GetInfoFromIWebSocketConnection(socket);
                if (info == null || string.IsNullOrEmpty(info.Token))
                {
                    //(logMsg + $"错误:未获取到帐号信息!").ExLogError();
                    return;
                }
                if (string.IsNullOrEmpty(message)) return;
                //添加数据库数据
                try
                {
                    var cr = JsonHelper.DeserializeObject<WebSocketMessage<dynamic>>(message);
                    if (cr.EnumWebSocketMessage == EnumWebSocketMessage.普通消息)
                    {
                        SendAll(cr);
                    }
                    else
                    {
                        (logMsg + $"错误:消息类型参数EnumChatSendType不对").ExLogError();
                        return;
                    }
                }
                catch (Exception e)
                {
                    e.ExLogError(logMsg);
                    return;
                }
            });
        }

        /// <summary>
        /// 断开
        /// </summary>
        /// <param name="socket"></param>
        private static void _webSocketHelper_ClosingEvent(IWebSocketConnection socket)
        {
            Task.Run(() =>
            {
                try
                {
                    var info = GetInfoFromIWebSocketConnection(socket);
                    var temp = new WebSocketMessage<bool>()
                    {
                        EnumWebSocketMessage = EnumWebSocketMessage.更新状态消息,
                        Result = false,
                        Message = info.ID.ToString(),
                    };
                    SendAll(temp);
                }
                catch (Exception e)
                {
                    e.ExLogError($"websocket关闭出错:{DateTime.Now.ExToLong()}");
                }
            });
        }

        /// <summary>
        /// 连接上
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        private static bool _webSocketHelper_ConnectingEvent(IWebSocketConnection socket)
        {
            Task.Run(() =>
            {
                try
                {
                    var info = GetInfoFromIWebSocketConnection(socket);
                    var temp = new WebSocketMessage<bool>()
                    {
                        EnumWebSocketMessage = EnumWebSocketMessage.更新状态消息,
                        Result = true,
                        Message = info.ID.ToString()
                    };
                    SendAll(temp);
                }
                catch (Exception e)
                {
                    e.ExLogError($"websocket连接出错:{DateTime.Now.ExToLong()}");
                }
            });
            return true;
        }

        private static object _syncGetInfoFromIWebSocketConnection = new object();

        #endregion

        #region 解析消息

        /// <summary>
        /// 解析消息
        /// 判断对象属性PhoneUserCodeOrOpenID是否为null
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        internal static WebSocketInfo GetInfoFromIWebSocketConnection(IWebSocketConnection item)
        {
            try
            {
                NameValueCollection nameValue = item.ConnectionInfo.Path.ExToNameValueCollection();
                return GetTokenInfo(nameValue);
            }
            catch (Exception e)
            {
                e.ExLogError($"方法GetInfoFromIWebSocketConnection报错:{item.ConnectionInfo.ExToJson()}");
                return new WebSocketInfo();
            }
        }

        private static WebSocketInfo GetTokenInfo(NameValueCollection nameValue)
        {
            lock (_syncGetInfoFromIWebSocketConnection)
            {
                const int timeout = 60 * 10;
                if (nameValue.AllKeys.Contains(PcLoginedBll.PcTokenKey))
                {
                    string tokenKey = PcLoginedBll.PcTokenKey;
                    string token = nameValue[tokenKey];
                    if (string.IsNullOrEmpty(token)) return new WebSocketInfo();
                    string cacheKey = token;
                    var info = CacheHelper.GetCache(cacheKey) as WebSocketInfo;
                    if (info != null && info.ID > 0) return info;
                    token = System.Web.HttpUtility.UrlDecode(token);
                    lock (_syncGetInfoFromIWebSocketConnection)
                    {
                        try
                        {
                            var tempArray = token.Split(BaseBll.TokenSplit);
                            string encrypt = tempArray.Length > 1 ? tempArray[1] : null;
                            if (string.IsNullOrEmpty(encrypt)) return new WebSocketInfo();
                            string tokenBefore = DEncryptHelper.Decrypt(encrypt);
                            var array = tokenBefore.Split(BaseBll.TokenSplit);
                            if (array.Length < 5)
                            {
                                CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                return new WebSocketInfo();
                            }
                            int id = array[0].ExToInt32();
                            var r = new WebSocketInfo()
                            {
                                ID = id,
                                Token = token,
                                TokenKey = tokenKey,
                                EnumRunProject = EnumRunProject.系统后台端,
                                IsWechatToken = false,
                            };
                            using (var db = PcLoginedBll.DbNoFilter)
                            {
                                var dbModel = db.SysEmployee.AsNoTracking().Where(m => m.SysEmployeeID == id).Select(
                                    m => new
                                    {
                                        m.SysEmployeeName,
                                        m.SysEmployeePhone,
                                        m.HeaderUrl,
                                        SysStoreID = (int?)m.SysEmployeeStoreRelation.FirstOrDefault().SysStoreID
                                    }).FirstOrDefault();
                                if (dbModel == null || dbModel.SysStoreID == null)
                                {
                                    CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                    return new WebSocketInfo();
                                }

                                r.ID = id;
                                r.Name = dbModel.SysEmployeeName;
                                r.Phone = dbModel.SysEmployeePhone;
                                r.HeaderUrl = dbModel.HeaderUrl;
                                r.SysStoreID = dbModel.SysStoreID;
                                r.EnumOpenIDType = null;
                            }

                            CacheHelper.SetRelativeCache(cacheKey, r, timeout);
                            return r;
                        }
                        catch (Exception e)
                        {
                            CacheHelper.SetRelativeCache(token, new WebSocketInfo(), timeout);
                            return new WebSocketInfo();
                        }
                    }
                }

                if (nameValue.AllKeys.Contains(PcLoginedBll.WechatPcTokenKey))
                {
                    string tokenKey = PcLoginedBll.WechatPcTokenKey;
                    string token = nameValue[tokenKey];
                    if (string.IsNullOrEmpty(token)) return new WebSocketInfo();
                    string cacheKey = token;
                    var info = CacheHelper.GetCache(cacheKey) as WebSocketInfo;
                    if (info != null && info.ID > 0) return info;
                    token = System.Web.HttpUtility.UrlDecode(token);
                    lock (_syncGetInfoFromIWebSocketConnection)
                    {
                        try
                        {
                            var tempArray = token.Split(BaseBll.TokenSplit);
                            string encrypt = tempArray.Length > 1 ? tempArray[1] : null;
                            if (string.IsNullOrEmpty(encrypt)) return new WebSocketInfo();
                            string tokenBefore = DEncryptHelper.Decrypt(encrypt);
                            var array = tokenBefore.Split(BaseBll.TokenSplit);
                            if (array.Length < 5)
                            {
                                CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                return new WebSocketInfo();
                            }
                            int id = array[0].ExToInt32();
                            var r = new WebSocketInfo()
                            {
                                ID = id,
                                Token = token,
                                TokenKey = tokenKey,
                                EnumRunProject = EnumRunProject.系统后台端,
                                IsWechatToken = true,
                            };
                            using (var db = PcLoginedBll.DbNoFilter)
                            {
                                var dbModel = db.SysEmployeeOpenID.Where(m => m.ID == id).Select(
                                    m => new
                                    {
                                        m.nickname,
                                        m.headimgurl,
                                        m.OpenID,
                                        m.EnumOpenIDType
                                    }).FirstOrDefault();
                                if (dbModel == null)
                                {
                                    CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                    return new WebSocketInfo();
                                }

                                r.ID = id;
                                r.Name = dbModel.nickname;
                                //r.Phone = dbModel.SysEmployeePhone;
                                r.HeaderUrl = dbModel.headimgurl;
                                r.SysStoreID = null;//dbModel.SysStoreID;
                                r.EnumOpenIDType = dbModel.EnumOpenIDType;
                            }

                            CacheHelper.SetRelativeCache(cacheKey, r, timeout);
                            return r;
                        }
                        catch (Exception e)
                        {
                            CacheHelper.SetRelativeCache(token, new WebSocketInfo(), timeout);
                            return new WebSocketInfo();
                        }
                    }
                }

                if (nameValue.AllKeys.Contains(CustomerLoginedBll.CustomerTokenKey))
                {
                    string tokenKey = CustomerLoginedBll.CustomerTokenKey;
                    string token = nameValue[tokenKey];
                    if (string.IsNullOrEmpty(token)) return new WebSocketInfo();
                    string cacheKey = token;
                    var info = CacheHelper.GetCache(cacheKey) as WebSocketInfo;
                    if (info != null && info.ID > 0) return info;
                    token = System.Web.HttpUtility.UrlDecode(token);
                    lock (_syncGetInfoFromIWebSocketConnection)
                    {
                        try
                        {
                            var tempArray = token.Split(BaseBll.TokenSplit);
                            string encrypt = tempArray.Length > 1 ? tempArray[1] : null;
                            if (string.IsNullOrEmpty(encrypt)) return new WebSocketInfo();
                            string tokenBefore = DEncryptHelper.Decrypt(encrypt);
                            var array = tokenBefore.Split(BaseBll.TokenSplit);
                            if (array.Length < 5)
                            {
                                CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                return new WebSocketInfo();
                            }
                            int id = array[0].ExToInt32();
                            var r = new WebSocketInfo()
                            {
                                ID = id,
                                Token = token,
                                TokenKey = tokenKey,
                                EnumRunProject = EnumRunProject.微信客户端,
                                IsWechatToken = false,
                            };
                            using (var db = PcLoginedBll.DbNoFilter)
                            {
                                var dbModel = db.SysCustomer.AsNoTracking().Where(m => m.SysCustomerID == id).Select(
                                    m => new
                                    {
                                        m.SysCustomerName,
                                        m.Phone,
                                        m.HeaderUrl,
                                        SysStoreID = m.SysStoreID
                                    }).FirstOrDefault();
                                if (dbModel == null || dbModel.SysStoreID <= 0)
                                {
                                    CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                    return new WebSocketInfo();
                                }

                                r.Name = dbModel.SysCustomerName;
                                r.Phone = dbModel.Phone;
                                r.HeaderUrl = dbModel.HeaderUrl;
                                r.SysStoreID = dbModel.SysStoreID;
                                r.EnumOpenIDType = null;
                            }

                            CacheHelper.SetRelativeCache(cacheKey, r, timeout);
                            return r;
                        }
                        catch (Exception e)
                        {
                            CacheHelper.SetRelativeCache(token, new WebSocketInfo(), timeout);
                            return new WebSocketInfo();
                        }
                    }
                }

                if (nameValue.AllKeys.Contains(CustomerLoginedBll.WechatCustomerTokenKey))
                {
                    string tokenKey = CustomerLoginedBll.WechatCustomerTokenKey;
                    string token = nameValue[tokenKey];
                    if (string.IsNullOrEmpty(token)) return new WebSocketInfo();
                    string cacheKey = token;
                    var info = CacheHelper.GetCache(cacheKey) as WebSocketInfo;
                    if (info != null && info.ID > 0) return info;
                    token = System.Web.HttpUtility.UrlDecode(token);
                    lock (_syncGetInfoFromIWebSocketConnection)
                    {
                        try
                        {
                            var tempArray = token.Split(BaseBll.TokenSplit);
                            string encrypt = tempArray.Length > 1 ? tempArray[1] : null;
                            if (string.IsNullOrEmpty(encrypt)) return new WebSocketInfo();
                            string tokenBefore = DEncryptHelper.Decrypt(encrypt);
                            var array = tokenBefore.Split(BaseBll.TokenSplit);
                            if (array.Length < 5)
                            {
                                CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                return new WebSocketInfo();
                            }
                            int id = array[0].ExToInt32();
                            var r = new WebSocketInfo()
                            {
                                ID = id,
                                Token = token,
                                TokenKey = tokenKey,
                                EnumRunProject = EnumRunProject.微信客户端,
                                IsWechatToken = true,
                            };
                            using (var db = PcLoginedBll.DbNoFilter)
                            {
                                var dbModel = db.SysCustomerOpenID.Where(m => m.ID == id).Select(
                                    m => new
                                    {
                                        m.nickname,
                                        m.headimgurl,
                                        m.OpenID,
                                        m.EnumOpenIDType
                                    }).FirstOrDefault();
                                if (dbModel == null)
                                {
                                    CacheHelper.SetRelativeCache(cacheKey, new WebSocketInfo(), timeout);
                                    return new WebSocketInfo();
                                }

                                r.ID = id;
                                r.Name = dbModel.nickname;
                                //r.Phone = dbModel.SysEmployeePhone;
                                r.HeaderUrl = dbModel.headimgurl;
                                r.SysStoreID = null;//dbModel.SysStoreID;
                                r.EnumOpenIDType = dbModel.EnumOpenIDType;
                            }

                            CacheHelper.SetRelativeCache(cacheKey, r, timeout);
                            return r;
                        }
                        catch (Exception e)
                        {
                            CacheHelper.SetRelativeCache(token, new WebSocketInfo(), timeout);
                            return new WebSocketInfo();
                        }
                    }
                }

                return new WebSocketInfo();
            }
        }

        /// <summary>
        /// 4种.员工OpenID,员工信息,客户OpenID,客户信息
        /// </summary>
        internal class WebSocketInfo
        {
            public string TokenKey { get; set; }

            /// <summary>
            /// Token
            /// </summary>
            public string Token { get; set; }

            public bool IsWechatToken { get; set; }

            public EnumRunProject EnumRunProject { get; set; }

            /// <summary>
            /// 前端类型
            /// </summary>
            public EnumOpenIDType? EnumOpenIDType { get; set; }

            public int ID { get; set; }

            /// <summary>
            /// PC端微信没有StoreID
            /// </summary>
            public int? SysStoreID { get; set; }

            public string Name { get; set; }

            public string Phone { get; set; }

            public string HeaderUrl { get; set; }
        }

        #endregion

        #region 具体某个员工信息获取 和 断开连接

        /// <summary>
        /// 具体某个员工信息获取
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        internal static List<IWebSocketConnection> GetList(string token)
        {
            var r = new List<IWebSocketConnection>();
            List<IWebSocketConnection> list = _webSocketHelper.GetList();
            if (string.IsNullOrEmpty(token)) return r;
            foreach (IWebSocketConnection item in list)
            {
                var temp = GetInfoFromIWebSocketConnection(item);
                if (temp != null && !string.IsNullOrEmpty(temp.Token))
                {
                    if (temp.Token == token)
                    {
                        r.Add(item);
                        continue;
                    }
                }
            }

            return r;
        }

        internal static List<IWebSocketConnection> GetList(EnumRunProject enumRunProject)
        {
            var r = new List<IWebSocketConnection>();
            List<IWebSocketConnection> list = _webSocketHelper.GetList();
            foreach (IWebSocketConnection item in list)
            {
                var temp = GetInfoFromIWebSocketConnection(item);
                if (temp != null && !string.IsNullOrEmpty(temp.Token))
                {
                    if (enumRunProject == EnumRunProject.全部 || temp.EnumRunProject == enumRunProject)
                    {
                        r.Add(item);
                        continue;
                    }
                }
            }

            return r;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <param name="token"></param>
        public static void CloseThis(string token)
        {
            var list = GetList(token);
            if (list.Count == 0) return;
            foreach (var item in list)
            {
                item.Close();
            }
        }

        #endregion

        #region 发送消息

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="msgClass"></param>
        /// <param name="token"></param>
        public static AjaxResult<bool> Send<T>(WebSocketMessage<T> msgClass, string token)
        {
            AjaxResult<bool> r = new AjaxResult<bool>();
            if (_webSocketHelper == null)
            {
                r.Message = "未启动websocket";
                return r;
            }
            var allUser = GetList(token);
            var result = _webSocketHelper.SendAll(msgClass.ExToJson(), allUser);
            r.Success = result;
            r.Message = result ? "发送成功" : "发送失败";
            return r;
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="msgClass"></param>
        /// <param name="enumRunProject"></param>
        public static AjaxResult<bool> SendAll<T>(WebSocketMessage<T> msgClass, EnumRunProject enumRunProject = EnumRunProject.系统后台端)
        {
            AjaxResult<bool> r = new AjaxResult<bool>();
            if (_webSocketHelper == null)
            {
                r.Message = "未启动websocket";
                return r;
            }
            var allUser = GetList(enumRunProject);
            var result = _webSocketHelper.SendAll(msgClass.ExToJson(), allUser);
            r.Success = result;
            r.Message = result ? "发送成功" : "发送失败";
            return r;
        }

        #endregion

        #region 关闭WebSocket

        /// <summary>
        /// 关闭WebSocket
        /// </summary>
        public static void Close()
        {
            try
            {
                if (_webSocketHelper == null) return;
                _webSocketHelper.Close();
                _webSocketHelper = null;
                //$"{DateTime.Now.ExToLong()}咨询订单的websocket关闭成功".ExLog();
            }
            catch
            {
            }
        }

        #endregion
    }
}
